﻿#include "GLLInc.h"
#include "GLLVirtualWorld.h"
#include "GLLLevelManager.h"
#include "GLLOSApp.h"
#include "GLLConfig.h"
#include "GLLTheSystem.h"

//-----------------------------------------------------------------------------
#ifdef GLL_OGRE

#pragma comment( lib, "OgreMain.lib" )

namespace GLL
{
	class OgreListener : public Ogre::FrameListener
	{

	};
	static OgreListener ogreListener;

	VirtualWorld::VirtualWorld()
	{
		_lvlMgr = 0;
		_root = 0;
		_renderWindow = 0;
	}

	static Config CfgOgre3D("Ogre3D");

	void VirtualWorld::RenderOneFrame_spec()
	{
		LOCK_MUTEX(_renderMutex);
		try{
			_root->renderOneFrame();
		}
		catch(std::exception& e)
		{
			const char* desc = e.what();
			GLL_BreakPoint;
		}
	}

	void VirtualWorld::DoCreate_spec()
	{
		_root = OGRE_NEW Ogre::Root("", "", "Trace.3DWorld.log");
		_root->addFrameListener(&ogreListener);
		_LoadPluginsOgre();

		Ogre::RenderSystem* pRenderer = _ChooseRendererOgre();
		if(!pRenderer) GLL_Throw("No renderer loaded. GL or DX");

		_ConfigRenderSystemOgre(pRenderer);
		if(!_attachRendererToWindowOgre()) 
			GLL_Throw("attach renderer to window failed");
	}

	void VirtualWorld::DoDestroy_spec()
	{
		
	}

	void VirtualWorld::OnScreenSizeChanged_spec( Size2Di& size)
	{
		///通知Ogre窗口大小已经修改
		_renderWindow->resize(size.width, size.height);
		_renderWindow->windowMovedOrResized();
	}

	void VirtualWorld::_ConfigEnvironmentOgre()
	{
		//设置图片采样方式
		Ogre::FilterOptions opt_min = Ogre::FO_ANISOTROPIC;
		Ogre::FilterOptions opt_mag = Ogre::FO_ANISOTROPIC;
		Ogre::FilterOptions opt_mipmap = Ogre::FO_ANISOTROPIC;
		Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(opt_min, opt_mag, opt_mipmap);
		Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(4);

		//Ogre::TextureFilterOptions topt = Ogre::TFO_NONE;
		//Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(topt);

		Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
		//Ogre::MeshManager::getSingleton().setMemoryBudget(1024*1024*10);
		//Ogre::TextureManager::getSingleton().setMemoryBudget(1024*1024*10);
	}

	void VirtualWorld::_ConfigRenderSystemOgre( Ogre::RenderSystem* pRenderSystem )
	{
		Ogre::ConfigOptionMap mapOfg = pRenderSystem->getConfigOptions();

		const char* Cfg_Key;

		Cfg_Key = "Anti aliasing";
		String FSAA_Cfg = CfgOgre3D.GetValueStr(Cfg_Key);
		if(!FSAA_Cfg.empty())
		{
			//取得显卡抗锯齿到几(0, 2, 4, 6..);
			Ogre::ConfigOption AALvl = mapOfg[Cfg_Key];
			Ogre::StringVector::iterator Iter = AALvl.possibleValues.begin();
			Ogre::String FSAA;
			for (; Iter != AALvl.possibleValues.end(); ++Iter)
			{
				FSAA = *Iter;
				if(FSAA_Cfg == FSAA)
				{
					pRenderSystem->setConfigOption(Cfg_Key, FSAA_Cfg);
					break;
				}
			}
		}


		Cfg_Key = "VSync";
		String VSync_Cfg = CfgOgre3D.GetValueStr(Cfg_Key);
		if(!VSync_Cfg.empty())
		{
			pRenderSystem->setConfigOption(Cfg_Key, VSync_Cfg);
		}

	}

	void VirtualWorld::_LoadPluginsOgre()
	{
		StringVec plugins = CfgOgre3D.GetValueStrVec("plugin");
		BOOST_FOREACH(String& plugin, plugins)
		{
			_root->loadPlugin(plugin);
		}
	}

	Ogre::RenderSystem* VirtualWorld::_ChooseRendererOgre()
	{
		//设置渲染接口
		//1.6
		//Ogre::RenderSystemList* pList = _root->getAvailableRenderers(); 
		//Ogre::RenderSystemList::iterator it = pList->begin();
		//if(it == pList->end()) return false;

		//1.7
		const Ogre::RenderSystemList& pList = _root->getAvailableRenderers(); 
		Ogre::RenderSystemList::const_iterator it = pList.begin();
		if(it == pList.end()) return 0;

		//Only 1 rendersystem our game is using
		Ogre::RenderSystem* pRenderSystem = *it; 
		if (!pRenderSystem)	return 0;
		_root->setRenderSystem(pRenderSystem);

		return pRenderSystem;
	}

	bool VirtualWorld::_attachRendererToWindowOgre( )
	{
		ulong osWndPtr = 0;

#ifdef GLL_WIN
		osWndPtr = (ulong)theApp()->GetHWND();
#endif

		if(!osWndPtr) GLL_Throw("Creation of OS Window was needed of the creation of virtual world");
		Ogre::NameValuePairList param;
		param["left"] = "0";
		param["top"] = "0";
		param["title"] = "";
		param["externalWindowHandle"] =Ogre:: StringConverter::toString(osWndPtr);
		_root->initialise(false);

		//move to camera
		Size2Di s = OSApp::Ref().GetWindowInitialSize();
		_renderWindow = _root->createRenderWindow("RenderTargetWindow", s.width, s.height, false, &param);
		if (!_renderWindow) return false;

		_ConfigEnvironmentOgre();

		return true;
	}

	bool VirtualWorld::SetQuality_spec( Quality )
	{
		return true;
	}
	//std::vector<Ogre::String> VirtualWorld::CollisionCheck( SceneID sid, const Ogre::Ray& ray, unsigned int m)
	//{
	//	std::vector<Ogre::String> pTempVec ;
	//	pTempVec.clear() ;

	//	Ogre::RaySceneQuery* rsq =  SM(sid)->createRayQuery(ray);
	//	if(!rsq) return pTempVec;
	//	rsq->setSortByDistance(true);
	//	rsq->setQueryTypeMask(Ogre::SceneManager::ENTITY_TYPE_MASK);
	//	rsq->setQueryMask(m);
	//	Ogre::RaySceneQueryResult result = rsq->execute();
	//	Ogre::RaySceneQueryResult::iterator Iter;

	//	for ( Iter = result.begin(); Iter != result.end(); ++Iter)
	//	{
	//		if(Iter->movable)
	//		{
	//			const Ogre::String& name = Iter->movable->getName();
	//			pTempVec.push_back(name);
	//		}
	//	}

	//	return pTempVec ;
	//}

	//bool VirtualWorld::GetMouseRay(Ogre::Camera* pCam, Ogre::Ray& ray, float X, float Y)
	//{
	//	if(!pCam) return false;
	//	Size2Df s = GetClientSize();
	//	pCam->getCameraToViewportRay(X / (float)s.cx, Y / (float)s.cy, &ray);
	//	return true;
	//}

	////Ogre::ParticleSystem* VirtualWorld::RetrieveParticleSystem( SceneID sid, const Ogre::String& name, const Ogre::String& script )
	////{
	////	Ogre::SceneManager& sm = SM(sid);
	////	if(sm->hasParticleSystem(name)) return sm->getParticleSystem(name);
	////	Ogre::ParticleSystem* pParty = sm->createParticleSystem(name, script);
	////	return pParty;
	////}


	//Ogre::Vector2 VirtualWorld::Pos3DTo2D( Ogre::Position3Df _3dPos, Ogre::Camera* pCam)
	//{
	//	Ogre::Vector2 vec(0, 0);
	//	if(!pCam) return vec;
	//	Ogre::Vector4 pos(_3dPos); 
	//	const Ogre::Matrix4& proj_mat = pCam->getProjectionMatrix();
	//	const Ogre::Matrix4& view_mat = pCam->getViewMatrix();
	//	Ogre::Vector4 mNonHCS =  proj_mat * (view_mat * pos); 
	//	Ogre::Vector4 mHCS(	mNonHCS.x / mNonHCS.w, mNonHCS.y / mNonHCS.w, mNonHCS.z / mNonHCS.w, 1.0f); 
	//	vec.x = mHCS.x * 0.5f + 0.5f;
	//	vec.y = 1.0f - (mHCS.y * 0.5f + 0.5f);
	//	return vec;
	//}


	//void VirtualWorld::SetRenderCamera( Ogre::Camera* pCam)
	//{
	//	_renderWindow->removeViewport(0);
	//	_renderWindow->addViewport(pCam);
	//}

	//Ogre::Camera* VirtualWorld::CreateCamera( SceneID smid, const Ogre::String& name)
	//{
	//	if(smid == SMID_NULL) return 0;
	//	Ogre::SceneManager* sm = SM(smid);
	//	Ogre::Camera* pCam = sm->createCamera(name);
	//	return pCam;
	//}

	//Ogre::SceneNode* VirtualWorld::CreateSceneNode( SceneID smid, const Ogre::String& name)
	//{
	//	if(smid == SMID_NULL) return 0;
	//	Ogre::SceneManager* sm = SM(smid); if(!sm) return 0;
	//	if(sm->hasSceneNode(name)) return 0;
	//	Ogre::SceneNode* pSN = sm->getRootSceneNode()->createChildSceneNode(name);
	//	return pSN;
	//}

	//bool VirtualWorld::HasSceneNode( SceneID smid, const Ogre::String& name )
	//{
	//	if(smid == SMID_NULL) return false;
	//	Ogre::SceneManager* sm = SM(smid);
	//	return sm->hasSceneNode(name);	
	//}

	//Ogre::SceneNode* VirtualWorld::CreateEntity( SceneID smid, const Ogre::String& name, const Ogre::String& mesh, Ogre::Entity** out )
	//{
	//	if(smid == SMID_NULL) return 0;
	//	Ogre::SceneManager* sm = SM(smid); if(!sm) return 0;
	//	Ogre::String node_name(name + ".NODE");
	//	if(sm->hasSceneNode(node_name)) return 0;
	//	Ogre::SceneNode* pSN = sm->getRootSceneNode()->createChildSceneNode(node_name);
	//	Ogre::Entity* pEnt = sm->createEntity(name, mesh);
	//	pSN->attachObject(pEnt);
	//	if(out) *out = pEnt;
	//	return pSN;
	//}

	//void VirtualWorld::DestroySceneNode( SceneID smid, Ogre::SceneNode* pNode)
	//{
	//	if(!pNode) return;
	//	//pNode->detachAllObjects(); //will be done in ~SceneNode();
	//	if(smid == SMID_NULL) return;
	//	Ogre::SceneManager* sm = SM(smid);
	//	pNode->removeAndDestroyAllChildren();
	//	sm->destroySceneNode(pNode);
	//}


	//void VirtualWorld::CeateSun( Ogre::Light* )
	//{
	//	Ogre::Light* l = SM(sid)->createLight("MainDirLite");
	//	l->setType(Ogre::Light::LT_DIRECTIONAL);
	//	l->setVisible(false);
	//}

	//void VirtualWorld::OnDefaultDirectionLightCreate(Ogre::Light* l)
	//{
	//	DBRecord rec;
	//	DBRet ret = DBExecute(DBData, "select * from Light", rec);
	//	if(!rec.FetchData()) return;
	//	bool on = rec.GetValue();
	//	if(!on) return;

	//	float dx = rec.GetValue();
	//	float dy = rec.GetValue();
	//	float dz = rec.GetValue();
	//	Ogre::Position3Df dir(dx,dy,dz);
	//	dir.normalise();
	//	l->setDirection(dir);
	//	GString Specular = rec.GetValue(); 
	//	GString Diffuse = rec.GetValue();
	//	char* Next=  0;
	//	float SpeR = (float)strtol(Specular, &Next, 16);
	//	float SpeB = (float)strtol(Next+1, &Next, 16);
	//	float SpeG = (float)strtol(Next+1, &Next, 16);
	//	int DiffR = strtol(Specular, &Next, 16);
	//	int DiffB = strtol(Next+1, &Next, 16);
	//	int DiffG = strtol(Next+1, &Next, 16);

	//	l->setVisible(on);
	//	l->setRenderingDistance(100);
	//	l->setSpecularColour(Ogre::ColourValue(SpeR/255.0f, SpeB/255.0f, SpeG/255.0f));
	//	l->setDiffuseColour(Ogre::ColourValue(DiffR/255.0f, DiffB/255.0f, DiffG/255.0f));
	//}

	//bool VirtualWorld::RaycastFromPoint( const std::vector<Ogre::Entity*>& vecEnt, const Ogre::Ray& ray, Ogre::String& OutName, Ogre::Position3Df& result )
	//{
	//	if(vecEnt.empty()) return false;

	//	// at this point we have raycast to a series of different objects bounding boxes.
	//	// we need to test these different objects to see which is the first polygon hit.
	//	// there are some minor optimizations (distance based) that mean we wont have to
	//	// check all of the objects most of the time, but the worst case scenario is that
	//	// we need to test every triangle of every object.
	//	Ogre::Real closest_distance = -1.0f;
	//	Ogre::Position3Df closest_result;
	//	Ogre::Entity *pentity = NULL;

	//	for(int i = 0; i < (int)vecEnt.size(); ++i)
	//	{

	//		// stop checking if we have found a raycast hit that is closer
	//		// than all remaining entities
	//		if (closest_distance >= 0.0f)
	//		{
	//			break;
	//		}

	//		// only check this result if its a hit against an entity
	//		// get the entity to check
	//		pentity = vecEnt[i];

	//		// mesh data to retrieve         
	//		size_t vertex_count;
	//		size_t index_count;
	//		Ogre::Position3Df *vertices;

	//		Ogre::uint32* indices;
	//		GetMeshInformation(pentity, vertex_count, vertices, index_count, indices,             
	//			pentity->getParentNode()->_getDerivedPosition(),
	//			pentity->getParentNode()->_getDerivedOrientation(),
	//			pentity->getParentNode()->_getDerivedScale());

	//		// test for hitting individual triangles on the mesh
	//		bool new_closest_found = false;
	//		for (int i = 0; i < static_cast<int>(index_count); i += 3)
	//		{
	//			// check for a hit against this triangle
	//			std::pair<bool, Ogre::Real> hit = Ogre::Math::intersects(ray, vertices[indices[i]],
	//				vertices[indices[i+1]], vertices[indices[i+2]], true, false);

	//			// if it was a hit check if its the closest
	//			if (hit.first)
	//			{
	//				if ((closest_distance < 0.0f) ||
	//					(hit.second < closest_distance))
	//				{
	//					// this is the closest so far, save it off
	//					closest_distance = hit.second;
	//					new_closest_found = true;
	//				}
	//			}
	//		}

	//		// free the verticies and indicies memory
	//		delete[] vertices;
	//		delete[] indices;

	//		// if we found a new closest raycast for this object, update the
	//		// closest_result before moving on to the next object.
	//		if (new_closest_found)
	//		{
	//			closest_result = ray.getPoint(closest_distance);               
	//		}     
	//	}

	//	// return the result
	//	if (closest_distance >= 0.0f)
	//	{
	//		OutName = pentity->getName();
	//		// raycast success
	//		result = closest_result;
	//		return (true);
	//	}
	//	else
	//	{
	//		// raycast failed
	//		return (false);
	//	}
	//}

	//void VirtualWorld::GetMeshInformation( Ogre::Entity *entity, size_t &vertex_count, Ogre::Position3Df* &vertices, size_t &index_count, Ogre::uint32* &indices, const Ogre::Position3Df &position, const Ogre::Quaternion &orient, const Ogre::Position3Df &scale )
	//{
	//	bool added_shared = false;
	//	size_t current_offset = 0;
	//	size_t shared_offset = 0;
	//	size_t next_offset = 0;
	//	size_t index_offset = 0;
	//	vertex_count = index_count = 0;

	//	Ogre::MeshPtr mesh = entity->getMesh();


	//	bool useSoftwareBlendingVertices = entity->hasSkeleton();

	//	if (useSoftwareBlendingVertices)
	//	{
	//		entity->_updateAnimation();
	//	}

	//	// Calculate how many vertices and indices we're going to need
	//	int iTestNumMeshs = mesh->getNumSubMeshes();
	//	int _iTestNumMeshs = entity->getNumSubEntities();
	//	for (unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
	//	{
	//		Ogre::SubMesh* submesh = mesh->getSubMesh( i );

	//		// We only need to add the shared vertices once
	//		if(submesh->useSharedVertices)
	//		{
	//			if( !added_shared )
	//			{
	//				vertex_count += mesh->sharedVertexData->vertexCount;
	//				added_shared = true;
	//			}
	//		}
	//		else
	//		{
	//			vertex_count += submesh->vertexData->vertexCount;
	//		}

	//		// Add the indices
	//		index_count += submesh->indexData->indexCount;
	//	}


	//	// Allocate space for the vertices and indices
	//	vertices = new Ogre::Position3Df[vertex_count];
	//	indices = new Ogre::uint32[index_count];

	//	added_shared = false;

	//	// Run through the submeshes again, adding the data into the arrays
	//	for ( unsigned short i = 0; i < mesh->getNumSubMeshes(); ++i)
	//	{
	//		Ogre::SubMesh* submesh = mesh->getSubMesh(i);

	//		//----------------------------------------------------------------
	//		// GET VERTEXDATA
	//		//----------------------------------------------------------------
	//		Ogre::VertexData* vertex_data;

	//		//When there is animation:
	//		if(useSoftwareBlendingVertices)
	//			vertex_data = submesh->useSharedVertices ? entity->_getSkelAnimVertexData() : entity->getSubEntity(i)->_getSkelAnimVertexData();
	//		else
	//			vertex_data = submesh->useSharedVertices ? mesh->sharedVertexData : submesh->vertexData;


	//		if((!submesh->useSharedVertices)||(submesh->useSharedVertices && !added_shared))
	//		{
	//			if(submesh->useSharedVertices)
	//			{
	//				added_shared = true;
	//				shared_offset = current_offset;
	//			}

	//			const Ogre::VertexElement* posElem =
	//				vertex_data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);

	//			Ogre::HardwareVertexBufferSharedPtr vbuf =
	//				vertex_data->vertexBufferBinding->getBuffer(posElem->getSource());

	//			unsigned char* vertex =
	//				static_cast<unsigned char*>(vbuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));

	//			// There is _no_ baseVertexPointerToElement() which takes an Ogre::Real or a double
	//			//  as second argument. So make it float, to avoid trouble when Ogre::Real will
	//			//  be comiled/typedefed as double:
	//			//      Ogre::Real* pReal;
	//			float* pReal;

	//			for( size_t j = 0; j < vertex_data->vertexCount; ++j, vertex += vbuf->getVertexSize())
	//			{
	//				posElem->baseVertexPointerToElement(vertex, &pReal);

	//				Ogre::Position3Df pt(pReal[0], pReal[1], pReal[2]);

	//				vertices[current_offset + j] = (orient * (pt * scale)) + position;
	//			}

	//			vbuf->unlock();
	//			next_offset += vertex_data->vertexCount;
	//		}


	//		Ogre::IndexData* index_data = submesh->indexData;
	//		size_t numTris = index_data->indexCount / 3;
	//		Ogre::HardwareIndexBufferSharedPtr ibuf = index_data->indexBuffer;

	//		bool use32bitindexes = (ibuf->getType() == Ogre::HardwareIndexBuffer::IT_32BIT);

	//		void* hwBuf = ibuf->lock(Ogre::HardwareBuffer::HBL_READ_ONLY);

	//		size_t offset = (submesh->useSharedVertices)? shared_offset : current_offset;
	//		size_t index_start = index_data->indexStart;
	//		size_t last_index = numTris*3 + index_start;

	//		if (use32bitindexes) {
	//			Ogre::uint32* hwBuf32 = static_cast<Ogre::uint32*>(hwBuf);
	//			for (size_t k = index_start; k < last_index; ++k)
	//			{
	//				indices[index_offset++] = hwBuf32[k] + static_cast<Ogre::uint32>( offset );
	//			}
	//		} else {
	//			Ogre::uint16* hwBuf16 = static_cast<Ogre::uint16*>(hwBuf);
	//			for (size_t k = index_start; k < last_index; ++k)
	//			{
	//				indices[ index_offset++ ] = static_cast<Ogre::uint32>( hwBuf16[k] ) +
	//					static_cast<Ogre::uint32>( offset );
	//			}
	//		}

	//		ibuf->unlock();
	//		current_offset = next_offset;
	//	}
	//}

} //GLL


#endif